package com.cy;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

//@EnableDiscoveryClient //启动服务的发现机制
@EnableFeignClients //启动Feign方式的配置
@SpringBootApplication
public class NacosConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(NacosConsumerApplication.class,args);
    }
    /**
     *RestTemplate封装了Http请求方式，可以直接通过此对象远程访问另一个http服务
     * 这个对象springboot工程启动时没有提供初始化
     * 需要手动创建这个对象，并将此对象交给spring管理
     * RestTemplate是跨进程的调用
     * @Bean 应用说明：
     * 1）用于在@Configuration注解描述的类中描述方法
     * 2）此注解描述的方法用于创建一个 第三方对象 并交给spring管理
     * 3）Spring存储此对象时，默认为bean起的名字为方法名
     */
    @Bean
    //@LoadBalanced //让RestTemplate对象实现 负载均衡 特性
    public RestTemplate restTemplate(){
        System.out.println("====restTemplate=====");//验证服务启动时是否直接初始化
        return new RestTemplate();
    }

    /*@Bean
    public RestTemplate RestTemplate(){
        return new RestTemplate();
    }*/

    @Bean
    @LoadBalanced //该注解底层触发拦截器，调用loadBalancerClient对象来获取ServiceInstance实例
    public RestTemplate loadBalancedRestTemplate(){
        return new RestTemplate();
    }

    @Autowired
    private RestTemplate loadBalancedRestTemplate;

    @RestController
    public class ConsumerController{

        @Value("${spring.application.name}")
        private String appName;

        /**
         * 此LoadBalancerClient对象可以基于服务名找到具体的服务实例（ServiceInstance）
         * 获取实例后可以获取实例对应的ip地址（host）和端口号（port）
         * 再通过restTemplate调用对应的服务
         */

        @Autowired
        private LoadBalancerClient loadBalancerClient;

        @Autowired
        private RestTemplate restTemplate;

        @Autowired
        private ConsumerService consumerService;

        private AtomicLong atomicLong = new AtomicLong(0);

        // http://localhost:8090/consumer/doRestEcho/
        @GetMapping("/consumer/doRestEcho")
        public String doRestEcho(){

            consumerService.doGetResource();

            /*ServiceInstance serviceInstance =
                    loadBalancerClient.choose("nacos-provider");*/
           /* String url = String.format("http://%s:%s/provider/echo/%s",
                    serviceInstance.getHost(), //服务实例的ip地址
                    serviceInstance.getPort(), //服务实例的端口号
                    appName);*/

            //通过服务名调用 -- 配置服务发现 -- 配置负载均衡
            String url="http://nacos-provider/provider/echo/"+appName;

            System.out.println("request url:"+url);
            //getForObject(String url,Class<T> responseType)
            //通过该方法请求服务
            return loadBalancedRestTemplate.getForObject(
                    url, //请求服务的地址
                    String.class);//请求服务的响应结果类型(responseType)
        }

        /**
         * 通过loadBalancerClient获取服务实例,然后构建url,再通过
         * restTemplate进行访问,可以实现负载均衡,优势是直观,但代码量
         * 稍微多一些.
         * @return
         */
        @GetMapping("/consumer/doRestEcho2")
        public String doRestLoadBalancerClientEcho(){
            //基于服务名获取服务实例 (不同服务实例端口肯定不同),构建url
            ServiceInstance serviceInstance=//8081,8082
                    loadBalancerClient.choose("nacos-provider");
            String serviceIp=serviceInstance.getHost();//ip
            int servicePort=serviceInstance.getPort();//port
            String url=String.format("http://%s:%s/provider/echo/%s",serviceIp,servicePort,appName);
            //向服务提供方发起http请求,获取响应数据
            return restTemplate.getForObject(
                    url,//要请求的服务的地址
                    String.class);//String.class为请求服务的响应结果类型(responseType)
        }


        /**
         * 在RestTemplate对象构建时,通过@LoadBalance注解,告诉spring框架
         * 赋予RestTemplate对象负载均衡特性.在定义要访问的服务url时,直接
         * 可以通过服务名找到对应的服务实例进行访问.
         * @return
         */
        @GetMapping("/consumer/doRestEcho3")
        public String doLoadBalanceRestTemplateEcho(){

            try {
                //休眠的两种方式，模拟慢调用
                //Thread.sleep(200);
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {}

            //用于测试预热
            System.out.println(atomicLong.getAndIncrement());

            String url=String.format("http://nacos-provider/provider/echo/%s",appName);
            //向服务提供方发起http请求,获取响应数据
            return loadBalancedRestTemplate.getForObject(
                    url,//要请求的服务的地址
                    String.class);//String.class为请求服务的响应结果类型(responseType)
        }

        /**
         * Sentinel热点测试
         * 定义一个基于指定id获取数据的方法，通过此方法
         * 演示基于热点数据的限流
         */
        @GetMapping("/consumer/doFindById/{id}")
        @SentinelResource("findById")
        public String doFindById(@PathVariable Integer id){
            return "the hot resource id is "+id;
        }

        @GetMapping("/consumer/doFindById2")
        @SentinelResource("findById2")
        public String doFindById2(@RequestParam Integer id){
            return "the hot resource id is "+id;
        }

    }
}
